﻿#region Using's

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Threading;

using MyUtils;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Test.MyUtils.TestHelp;

#endregion

namespace Test.MyUtils
{
	[TestClass]
	public class TestUtils : FixtureMsTest
	{

		object Null = null;

		readonly IEnumerable<int> colEmpty = new int[0];
		IEnumerable<int> colNull = null;

		readonly IEnumerable<int> colNotEmpty = new[]
		{
			1
		};

		readonly object valueExist = 5;
		readonly object valueDefault = 0;

		class testClone : Cloneable<testClone>
		{
			public readonly object someRef = new object();
			public string someString = "asd";
			public int val = 5;
		}

		static void checkClone(testClone test, testClone clone1)
		{
			Assert.AreEqual(test.val, clone1.val);
			Assert.AreEqual(test.someString, clone1.someString);
			Assert.AreEqual(test.someRef, clone1.someRef);
		}

		public class Perc
		{
			public float Value;

			public static Perc ConvertToThis(IConvertible source)
			{
				return new Perc
				{
					Value = source.ToSingle(null)
				};
			}

			public override string ToString()
			{
				return Value * 100 + "%";
			}
		}

		public class Perc2
		{
			public int Value;
		}

		[TestMethod]
		public void Should_convert_simple_string_to_int()
		{
			Assert.AreEqual(3, "3".ToNumber());
			Assert.AreEqual(35, "35".ToNumber());
			Assert.AreEqual(356666, "356666".ToNumber());
			Assert.AreEqual(null, "-35".ToNumber());
			Assert.AreEqual(null, "".ToNumber());
			Assert.AreEqual(null, Utils.ToNumber(null));
			Assert.AreEqual(null, "asd".ToNumber());
			Assert.AreEqual(null, "a".ToNumber());
		}

		[TestMethod]
		public void Should_allow_check_existance_of_value_type_comparing_with_default()
		{
			Assert.IsTrue(valueExist.Exists());
			Assert.IsFalse(valueDefault.Exists());
		}

		[TestMethod]
		public void Should_allow_convert_custom_types_from_Convertable()
		{
			var perc = .1.ConvertTo<Perc>();
			Equal("10%", perc.ToString());
		}

		[TestMethod]
		public void Should_allow_convert_custom_types_from_Convertable2()
		{
			Throws<NotSupportedException>(delegate { _ = .1.ConvertTo<Perc2>(); });
		}

		//[TestMethod]
		//public void Should_extend_conversions() {
		//    Assert.AreEqual(5.2, new decimal(5.2).ConvertTo<double>());

		//    Thread.CurrentThread.CurrentCulture = new CultureInfo("ru-ru");
		//    Assert.AreEqual(5.3, "5,3".ConvertTo<double>());

		//    Thread.CurrentThread.CurrentCulture = new CultureInfo("en-us");
		//    Assert.AreEqual(5.4, "5.4".ConvertTo<double>());
		//}

		//class testConvert : IConvertible {
		//    #region IConvertible
		//    public TypeCode GetTypeCode() {
		//        throw new NotImplementedException();
		//    }

		//    public bool ToBoolean(IFormatProvider provider) {
		//        throw new NotImplementedException();
		//    }

		//    public byte ToByte(IFormatProvider provider) {
		//        throw new NotImplementedException();
		//    }

		//    public char ToChar(IFormatProvider provider) {
		//        throw new NotImplementedException();
		//    }

		//    public DateTime ToDateTime(IFormatProvider provider) {
		//        throw new NotImplementedException();
		//    }

		//    public decimal ToDecimal(IFormatProvider provider) {
		//        throw new NotImplementedException();
		//    }

		//    public double ToDouble(IFormatProvider provider) {
		//        throw new NotImplementedException();
		//    }

		//    public short ToInt16(IFormatProvider provider) {
		//        throw new NotImplementedException();
		//    }

		//    public int ToInt32(IFormatProvider provider) {
		//        throw new NotImplementedException();
		//    }

		//    public long ToInt64(IFormatProvider provider) {
		//        throw new NotImplementedException();
		//    }

		//    public sbyte ToSByte(IFormatProvider provider) {
		//        throw new NotImplementedException();
		//    }

		//    public float ToSingle(IFormatProvider provider) {
		//        throw new NotImplementedException();
		//    }

		//    public string ToString(IFormatProvider provider) {
		//        throw new NotImplementedException();
		//    }

		//    public object ToType(Type conversionType, IFormatProvider provider) {
		//        throw new NotImplementedException();
		//    }

		//    public ushort ToUInt16(IFormatProvider provider) {
		//        throw new NotImplementedException();
		//    }

		//    public uint ToUInt32(IFormatProvider provider) {
		//        throw new NotImplementedException();
		//    }

		//    public ulong ToUInt64(IFormatProvider provider) {
		//        throw new NotImplementedException();
		//    }
		//    #endregion
		//}

		//[TestMethod]
		//public void Should_accurately_show_convertion_errors() {
		//    try {
		//        "test1".ConvertTo<int>();
		//    } catch (FormatException) { }

		//    try {
		//        long.MaxValue.ConvertTo<int>();
		//    } catch (OverflowException) { }

		//    Assert.AreEqual("5", 5.ConvertTo<string>());

		//    try {
		//        5.ConvertTo<testClone>();
		//    } catch (InvalidCastException) { }

		//    try {
		//        5.ConvertTo<testConvert>();
		//    } catch (InvalidCastException) { }
		//}

		[TestMethod]
		public void Should_allow_convert_to_full_time()
		{
			var dt = new DateTime(2000, 1, 2, 3, 4, 5, DateTimeKind.Utc);
			dt.AddMilliseconds(6789);
			Equal("2000-Jan-2 Sun 3:04 UTC", dt.ToStringFull());

			dt = dt.ToLocalTime();
			Match(@"^2000-Jan-2 Sun \d:04 UTC[+-]\d\d:00$", dt.ToStringFull());
		}

		[TestMethod]
		public void Should_allow_easy_works_with_custom_attributes()
		{
			Assert.IsTrue(typeof(int).Attribute<SerializableAttribute>().GetType() == typeof(SerializableAttribute));
		}

		[TestMethod]
		public void Should_allow_quicly_implement_memberwize_clone()
		{
			var test = new testClone();
			var clone1 = test.Clone();
			checkClone(test, clone1);
			var clone2 = (testClone)(test as ICloneable).Clone();
			checkClone(test, clone2);
		}

		[TestMethod]
		public void Should_allow_simply_check_for_empty()
		{
			Assert.IsFalse(colNull.Exists());
			Assert.IsFalse(colEmpty.Exists());
			Assert.IsTrue(colNotEmpty.Exists());
		}

		[TestMethod]
		public void Should_allow_simply_check_for_null()
		{
			Assert.IsTrue(new object().Exists());
			Assert.IsFalse(Null.Exists());
		}

		[TestMethod]
		public void Should_allow_to_combine_path_by_func()
		{
			var curBinPath = Path.GetDirectoryName(GetType().Assembly.Location);
			var test = Path.Combine(curBinPath, @"dir1\dir2\file.my");
			Assert.AreEqual(test, Utils.GetFileNameInBin("dir1/dir2/file.my"));
			Assert.AreEqual(test, Utils.GetFileNameInBin(@"dir1\dir2\file.my"));
		}

		[TestMethod]
		public void Should_extend_conversions()
		{
			Thread.CurrentThread.CurrentCulture = new CultureInfo("ru-ru");
			Assert.AreEqual(5.3, "5,3".ConvertTo<double>());

			Thread.CurrentThread.CurrentCulture = new CultureInfo("en-us");
			Assert.AreEqual(5.4, "5.4".ConvertTo<double>());
		}

		[TestMethod]
		public void Should_help_with_arg_null_handling()
		{
			try
			{
				Null.EnsureExists();
			}
			catch (SystemException)
			{
				return;
			}
			Assert.Fail();
		}

		[TestMethod]
		public void Should_help_with_arg_null_handling2()
		{
			new object().EnsureExists();
		}

		[TestMethod]
		public void Should_not_throw_exception_on_ensure()
		{
			true.Ensure();
		}

		[TestMethod]
		public void Should_not_throw_exception_with_message_on_ensure()
		{
			true.Ensure("test1");
		}

		[TestMethod]
		public void Should_provide_usable_max_extremum_detector()
		{
			DateTime d1 = new DateTime(2000, 1, 1),
				d2 = new DateTime(2000, 1, 2),
				d3 = new DateTime(2000, 1, 3),
				d4 = new DateTime(2000, 1, 4);

			Equal(d1, Utils.Min(d1, d2));
			Equal(d1, Utils.Min(d2, d1));
			Equal(d1, Utils.Min(d2, d2, d1, d4));

			Equal(d2, Utils.Max(d1, d2));
			Equal(d2, Utils.Max(d2, d1));
			Equal(d4, Utils.Max(d2, d2, d1, d4));
		}

		[TestMethod]
		public void Should_provide_way_to_create_inline_comparer()
		{
			var test = new List<int>
			{
				1,
				2,
				3
			};
			test.Sort((x, y) => x > y ? 1 : 0);
			CollectionAssert.AreEqual(new[]
			{
				3, 2, 1
			}, test);
		}

		[TestMethod]
		public void Should_return_assemblies_path_correctly()
		{
			var curBinPath =
				Path.GetDirectoryName(GetType().Assembly.CodeBase.Replace(@"file:///", string.Empty).Replace("/", "\\"));
			Assert.AreEqual(curBinPath, Utils.BinPath);
		}

		[TestMethod]
		public void Should_simplify_random_generating()
		{
			var dic = new Dictionary<int, int>();
			for (var i = 0; i < 1000; i++)
			{
				var test = 4.Random();
				Assert.IsTrue(test >= 0);
				Assert.IsTrue(test < 4);

				if (!dic.ContainsKey(i))
				{
					dic[i] = 0;
				}
				dic[i]++;
			}

			True(dic[0] > 0);
			True(dic[1] > 0);
			True(dic[2] > 0);
			True(dic[3] > 0);
		}

		[TestMethod]
		[ExpectedException(typeof(ContractException))]
		public void Should_throw_exception_on_ensure()
		{
			false.Ensure();
		}

		[TestMethod]
		public void Should_throw_exception_with_message_on_ensure()
		{
			try
			{
				false.Ensure("test1");
			}
			catch (ContractException ex)
			{
				Assert.IsTrue(ex.Message.Contains("test1"), ex.Message);
				return;
			}
			Assert.Fail();
		}
	}
}